/**
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Initial Developer of the Original Code is David Zuckerman
 * Portions created by the Initial Developer are Copyright (C) 2008
 * the Initial Developer. All Rights Reserved.
 *
 **/
package open.builder.codemodelviewer.ui.treeviewer;

import org.eclipse.core.runtime.IPath;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IPathEditorInput;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.DrillDownAdapter;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.texteditor.ITextEditor;

import com.adobe.flexbuilder.as.editor.ActionScriptEditor;
import com.adobe.flexbuilder.codemodel.common.CMFactory;
import com.adobe.flexbuilder.codemodel.project.IProject;
import com.adobe.flexbuilder.codemodel.tree.ASOffsetInformation;
import com.adobe.flexbuilder.codemodel.tree.IASNode;
import com.adobe.flexbuilder.codemodel.tree.IFileNode;
import com.adobe.flexide.mxml.core.editor.IMXMLEditor;

/**
 * Standard tree control generated by Eclipse
 * @author David Zuckerman
 * 
 */
public class CMTreeViewer extends ViewPart implements IViewPart {

	private final class WorkbenchListener implements ISelectionListener {
		public void selectionChanged(IWorkbenchPart arg0, ISelection arg1) {
			if ((arg0 instanceof ActionScriptEditor || arg0 instanceof IMXMLEditor) && arg1 instanceof ITextSelection) {
				if (fCurrentPath == null)
					initContent();

				if (fCurrentPath != null) {
					IPathEditorInput input = getCurrentIPathEditorInput();
					if (input != null) {
						IPath path = input.getPath();
						if (!fCurrentPath.equals(path)) {
							initContent();
						}

						synchronized (CMFactory.getLockObject()) {
							IProject projectForFile = CMFactory.getManager().getProjectForFile(path);
							if (projectForFile != null) {
								IFileNode fileNode = projectForFile.findFileNodeInProject(path);
								if (fileNode != null) {
									ASOffsetInformation info = new ASOffsetInformation(((ITextSelection) arg1).getOffset(), fileNode);
									IASNode containingNode = info.getContainingNode();
									if (containingNode != null) {
										try {
											fUpdateOnClick = false;
											fViewer.setSelection(new StructuredSelection(containingNode), true);
										} finally {
											fUpdateOnClick = true;
										}
									}
								}
							}
						}
					}

				}
			}

		}
	}

	private TreeViewer fViewer;
	private DrillDownAdapter fDrillDownAdapter;
	private IAction fRefreshAction;
	private IPath fCurrentPath = null;
	private boolean fUpdateOnClick = true;
	private WorkbenchListener fListener;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
	 */
	@Override
	public void createPartControl(Composite arg0) {
		fViewer = new TreeViewer(new Tree(arg0, SWT.SINGLE));
		fViewer.setContentProvider(new TreeContentProvider());
		fViewer.setLabelProvider(new TreeLabelProvider());
		fViewer.setAutoExpandLevel(0);
		fDrillDownAdapter = new DrillDownAdapter(fViewer);
		makeActions();
		hookContextMenu();
		hookDoubleClickAction();
		hookSingleClickAction();
		contributeToActionBars();
		fListener = new WorkbenchListener();
		getSite().getWorkbenchWindow().getSelectionService().addPostSelectionListener(fListener);
	}

	private void hookContextMenu() {
		MenuManager menuMgr = new MenuManager("#PopupMenu");
		menuMgr.setRemoveAllWhenShown(true);
		menuMgr.addMenuListener(new IMenuListener() {
			public void menuAboutToShow(IMenuManager manager) {
				CMTreeViewer.this.fillContextMenu(manager);
			}
		});
		Menu menu = menuMgr.createContextMenu(fViewer.getControl());
		fViewer.getControl().setMenu(menu);
		getSite().registerContextMenu(menuMgr, fViewer);
	}

	private void contributeToActionBars() {
		IActionBars bars = getViewSite().getActionBars();
		fillLocalPullDown(bars.getMenuManager());
		fillLocalToolBar(bars.getToolBarManager());
	}

	private void fillLocalPullDown(IMenuManager manager) {
		manager.add(fRefreshAction);
	}

	private void fillContextMenu(IMenuManager manager) {
		manager.add(fRefreshAction);
		manager.add(new Separator());
		fDrillDownAdapter.addNavigationActions(manager);
		// Other plug-ins can contribute there actions here
		manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
	}

	private void fillLocalToolBar(IToolBarManager manager) {
		manager.add(fRefreshAction);
		manager.add(new Separator());
		fDrillDownAdapter.addNavigationActions(manager);
	}

	IPathEditorInput getCurrentIPathEditorInput() {
		IWorkbenchPartSite site = getSite();
		if (site != null) {
			IWorkbenchPage page = site.getPage();
			if (page != null) {
				IEditorPart activeEditor = page.getActiveEditor();
				if (activeEditor != null) {
					IEditorInput editorInput = activeEditor.getEditorInput();
					if (editorInput instanceof IPathEditorInput) {
						return (IPathEditorInput) editorInput;
					}
				}
			}
		}
		return null;
	}

	ITextEditor getActiveTextEditor() {
		IWorkbenchPartSite site = getSite();
		if (site != null) {
			IWorkbenchPage page = site.getPage();
			if (page != null) {
				IEditorPart activeEditor = page.getActiveEditor();
				if (activeEditor != null && activeEditor instanceof ITextEditor) {
					return (ITextEditor) activeEditor;
				}
			}
		}
		return null;
	}

	private void initContent() {
		IPathEditorInput editorInput = getCurrentIPathEditorInput();
		if (editorInput != null) {
			IPath path = editorInput.getPath();
			synchronized (CMFactory.getLockObject()) {
				IProject projectForFile = CMFactory.getManager().getProjectForFile(path);
				if (projectForFile != null) {
					IFileNode fileNode = projectForFile.findFileNodeInProject(path);
					if (fileNode != null) {
						fViewer.setInput(fileNode);
						fViewer.refresh();
						fCurrentPath = path;
					}
				}
			}
		}
	}

	private void makeActions() {
		fRefreshAction = new Action() {

			@Override
			public void run() {
				initContent();
			}

		};
		fRefreshAction.setText("Refresh tree");
		ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
		fRefreshAction.setImageDescriptor(sharedImages.getImageDescriptor(ISharedImages.IMG_OBJS_INFO_TSK));
	}

	private void hookDoubleClickAction() {
		fViewer.addDoubleClickListener(new IDoubleClickListener() {
			public void doubleClick(DoubleClickEvent event) {
				//
			}
		});
	}

	private void hookSingleClickAction() {
		fViewer.addSelectionChangedListener(new ISelectionChangedListener() {
			public void selectionChanged(SelectionChangedEvent arg0) {
				if (fUpdateOnClick) {
					ISelection selection = fViewer.getSelection();
					if (selection instanceof IStructuredSelection) {
						Object element = ((IStructuredSelection) selection).getFirstElement();
						if (element instanceof IASNode && ((IASNode) element).getStart() >= 0) {
							ITextEditor textEditor = getActiveTextEditor();
							if (textEditor != null) {
								textEditor.selectAndReveal(((IASNode) element).getStart(), ((IASNode) element).getEnd() - ((IASNode) element).getStart());
							}
						}
					}
				}
			}

		});
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
	 */
	@Override
	public void setFocus() {
		// do nothing
	}

	@Override
	public void dispose() {
		getSite().getWorkbenchWindow().getSelectionService().removePostSelectionListener(fListener);
		fViewer = null;
		super.dispose();
	}

}
